RPG游戏制作 您所在的位置:网站首页 rpg npc设计 RPG游戏制作

RPG游戏制作

2024-07-15 16:48| 来源: 网络整理| 查看: 265

脚本触发方式分为Touch和Click,上一节实现了Touch,接下来实现Click。由于本游戏使用的是鼠标/手指来操纵人物,不存在什么虚拟摇杆,因此如果想要实现Click的话,就需要点击NPC,之后角色根据A星算法得到的路径行走,在NPC面前停下,然后对应NPC调用execute(),来调用对应的脚本。不过我们发现,目前的碰撞检测中是不包含NPC在内的,即无论NPC是否是PRIORITY_SAME,角色都会从NPC身上穿过,这显然不合理。因此,需要修改isPassing函数,来让它判断在一定范围内是否存在优先级为PRIORITY_SAME的NPC。

bool GameScene::isPassing(const Point& tilePos) { auto tiledMap = m_pMapLayer->getTiledMap(); auto mapSize = tiledMap->getMapSize(); //不可超出地图 if (tilePos.x < 0 || tilePos.x > (mapSize.width - 1) || tilePos.y > (mapSize.height - 1) || tilePos.y < 0) { return false; } auto layer = m_pMapLayer->getCollisionLayer(); auto gid = layer->getTileGIDAt(tilePos); bool ret = m_pMapLayer->isPassing(gid); //可通过则检查是否存在NPC if (ret) { auto tileSize = tiledMap->getTileSize(); //获取矩形 Rect r; r.origin = Point(tileSize.width * (tilePos.x + 0.5f), tileSize.height * (tilePos.y + 0.5f)); r.size = Size(1.f, 1.f); auto npc = m_pScriptLayer->getClickedNPC(r, PRIORITY_SAME); ret = ( npc == nullptr ? true : false); } return ret; } bool ret = m_pMapLayer->isPassing(gid); //可通过则检查是否存在NPC if (ret) { auto tileSize = tiledMap->getTileSize(); //获取矩形 Rect r; r.origin = Point(tileSize.width * (tilePos.x + 0.5f), tileSize.height * (tilePos.y + 0.5f)); r.size = Size(1.f, 1.f); auto npc = m_pScriptLayer->getClickedNPC(r, PRIORITY_SAME); ret = ( npc == nullptr ? true : false); } return ret; }

isPassing函数额外添加了一个判断,在对应图块可通过的情况下,会对当前的矩形来判断是否存在NPC,如果存在,则表示不可穿过。

NonPlayerCharacter* ScriptLayer::getClickedNPC(const Rect& r, int priority) { NonPlayerCharacter* npc = nullptr; auto it = find_if(m_npcList.begin(), m_npcList.end(), [&r, &priority](NonPlayerCharacter* npc) { return npc->getPriority() == priority && npc->intersectRect(r); }); if (it != m_npcList.end()) npc = *it; return npc; }

getClickedNPC()函数返回优先级相同并且矩阵发生碰撞的NPC,如果没有则返回nullptr。

接下来运行程序会发现,当直接点击优先级为PRIORITY_SAME的NPC时,人物没有动作。原因是GameScene::onTouchBegan()函数中检测到目的点不可通过,而应该认为该点可通过(前面的A星算法中默认目的点可到达就是为了当前这种情况)。

bool GameScene::onTouchBegan(Touch* touch,SDL_Event* event) { auto location = touch->getLocation(); auto tiledMap = m_pMapLayer->getTiledMap(); auto nodePos = tiledMap->convertToNodeSpace(location); auto tilePos = m_pMapLayer->convertToTileCoordinate(nodePos); auto player = m_pPlayerLayer->getPlayer();         (!this->isPassing(tilePos))//目标不可达 return false; //主角运动 player->moveToward(tilePos); return true; }

接下来就应该修改onTouchBegan();先判断目的点是否有NPC,如果有,则认为可通过(不清楚是否能到达),直接调用主角的moveToward()函数即可。

bool GameScene::onTouchBegan(Touch* touch,SDL_Event* event) { auto location = touch->getLocation(); auto tiledMap = m_pMapLayer->getTiledMap(); auto nodePos = tiledMap->convertToNodeSpace(location); auto tilePos = m_pMapLayer->convertToTileCoordinate(nodePos); auto player = m_pPlayerLayer->getPlayer(); //是否点击了相同优先级的NPC auto npc = m_pScriptLayer->getClickedNPC(Rect(nodePos, Size(1.f, 1.f)), PRIORITY_SAME); player->setTriggerNPC(npc); //TODO if (npc != nullptr) ; else if (!this->isPassing(tilePos))//目标不可达 return false; //主角运动 player->moveToward(tilePos); //显示点击特效 auto collisionLayer = m_pMapLayer->getCollisionLayer(); auto tileSize = tiledMap->getTileSize(); Point pos((tilePos.x + 0.5f) * tileSize.width, (tilePos.y + 0.3f) * tileSize.height); m_pEffectLayer->showAnimationEffect("click_path", pos, collisionLayer); return true; } //是否点击了相同优先级的NPC auto npc = m_pScriptLayer->getClickedNPC(Rect(nodePos, Size(1.f, 1.f)), PRIORITY_SAME); player->setTriggerNPC(npc); //TODO if (npc != nullptr) ; else if (!this->isPassing(tilePos))//目标不可达 return false; //主角运动 player->moveToward(tilePos); //显示点击特效 auto collisionLayer = m_pMapLayer->getCollisionLayer(); auto tileSize = tiledMap->getTileSize(); Point pos((tilePos.x + 0.5f) * tileSize.width, (tilePos.y + 0.3f) * tileSize.height); m_pEffectLayer->showAnimationEffect("click_path", pos, collisionLayer); return true; }

之后还需要修改Character类,来支持我们的操作。

NonPlayerCharacter* m_pTriggerNPC; //设置npc void setTriggerNPC(NonPlayerCharacter* npc);

Character类中添加一个NPC指针,在onTouchBegan()中,如果点击了NPC,则会把该NPC传送给主角。

void Character::popStepAndAnimate() { m_bMoving = false; //存在待到达目的点,转入 if (m_bHavePendingMove) { m_bHavePendingMove = false; this->clearShortestPath(); //滞后改变 this->moveToward(m_pendingMove); return ; }//运动结束 else if (m_nStepIndex >= m_shortestPath.size()) { this->clearShortestPath(); //站立动画 this->changeState(State::Idle); return ; }//点击了NPC,且将要到达 else if (m_pTriggerNPC != nullptr && m_nStepIndex == m_shortestPath.size() - 1) { auto delta = m_shortestPath.back()->getTilePos() - m_lastStep->getTilePos(); auto newDir = this->getDirection(delta); //改变方向 if (newDir != m_dir) { m_bDirty = true; m_dir = newDir; } this->clearShortestPath(); this->changeState(State::Idle); m_pTriggerNPC->execute(this->getUniqueID()); m_pTriggerNPC = nullptr; return ; }         //... } }//点击了NPC,且将要到达 else if (m_pTriggerNPC != nullptr && m_nStepIndex == m_shortestPath.size() - 1) { auto delta = m_shortestPath.back()->getTilePos() - m_lastStep->getTilePos(); auto newDir = this->getDirection(delta); //改变方向 if (newDir != m_dir) { m_bDirty = true; m_dir = newDir; } this->clearShortestPath(); this->changeState(State::Idle); m_pTriggerNPC->execute(this->getUniqueID()); m_pTriggerNPC = nullptr; return ; }         //... }

在这里判断下主角是否走到了对应NPC的面前,如果是的话,先判断主角朝向是否需要改变,然后停止运动,之后触发NPC的函数。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有